#include "ffmepgutils.h"
#include <QDebug>
#include <QFile>


#define ERRMSG(err_num) char errBuf[1024]; \
        av_strerror(err_num,errBuf,sizeof (errBuf));

#define IN_DATA_LEN 20480

#define REFILL_THREF 4096

static int checkSampleFormat(const AVCodec* codec,enum AVSampleFormat sampleFormat) {


    qDebug() << codec->name;
    const enum AVSampleFormat *fmts = codec->sample_fmts;

    while (*fmts != AV_SAMPLE_FMT_NONE) {

        if(*fmts == sampleFormat) {

            return 1;
        }

        fmts++;
    }


    return 0;
}

static int encode(AVCodecContext* ctx, AVFrame* frame, AVPacket* packet,QFile& outFile) {

    /**
     * @brief res
     *  * @return 0 on success, otherwise negative error code:
     *      AVERROR(EAGAIN):   input is not accepted in the current state - user
     *                         must read output with avcodec_receive_packet() (once
     *                         all output is read, the packet should be resent, and
     *                         the call will not fail with EAGAIN).
     *      AVERROR_EOF:       the encoder has been flushed, and no new frames can
     *                         be sent to it
     *      AVERROR(EINVAL):   codec not opened, refcounted_frames not set, it is a
     *                         decoder, or requires flush
     *      AVERROR(ENOMEM):   failed to add packet to internal queue, or similar
     *      other errors: legitimate encoding errors
     */
    int res = avcodec_send_frame(ctx,frame);


    if(res < 0) {

        ERRMSG(res);
        qDebug() << "avcodec_send_frame " << errBuf << " fail";
        return res;
    }

    while (true) {

        /** * @return 0 on success, otherwise negative error code:
         *      AVERROR(EAGAIN):   output is not available in the current state - user
         *                         must try to send input
         *      AVERROR_EOF:       the encoder has been fully flushed, and there will be
         *                         no more output packets
         *      AVERROR(EINVAL):   codec not opened, or it is a decoder
         *      other errors: legitimate encoding errors
        */
        res = avcodec_receive_packet(ctx,packet);

        if(res == AVERROR(EAGAIN) || AVERROR_EOF) {

            return 0;
        }else if(res < 0) {

            ERRMSG(res);
            qDebug() << "avcodec_receive_packet fail " << errBuf;
            return res;
        }

        outFile.write((char*)packet->data,packet->size);
        av_packet_unref(packet);
    }

    return 0;
}

static int decode(AVCodecContext* codecContext, AVPacket* packet, AVFrame* frame, QFile& outFile) {


    int res = avcodec_send_packet(codecContext,packet);

    if(res < 0) {

        ERRMSG(res);
        qDebug() << "avcodec_send_packet fail " << errBuf;
        return res;
    }

    while (true) {

        /*
         *   *      AVERROR(EAGAIN):   output is not available in this state - user must try
 *                         to send new input
 *      AVERROR_EOF:       the decoder has been fully flushed, and there will be
 *                         no more output frames
*/
        res = avcodec_receive_frame(codecContext,frame);

        if(res == AVERROR(EAGAIN) || res == AVERROR_EOF) {

            return 0;
        }else if (res < 0) {

            ERRMSG(res);
            qDebug() << "avcodec_receive_frame fail " << errBuf;
            return res;
        }

        outFile.write((char*)frame->data[0],frame->linesize[0]);
    }

    return 0;

}

FFMEPGUtils::FFMEPGUtils()
{

}

void FFMEPGUtils::pcmAACEncode(PCMSpec &pcmSpec)
{


    AVCodec* codec = avcodec_find_encoder_by_name("libfdk_aac");

    if(codec == nullptr) {

        qDebug() << "avcodec_find_decoder_by_name libfdk_aac fail";
        return;
    }

    qDebug() << codec->name;
    /*
     *  检测格式是否支持
    */
    if(!checkSampleFormat(codec,pcmSpec.sampleFormat)) {

        qDebug() << "sample format : " << av_get_sample_fmt_name(pcmSpec.sampleFormat) << " not support";
        // 进行音频重采样
        return;
    }


    AVCodecContext* codecContext = avcodec_alloc_context3(codec);

    if(codecContext == nullptr) {

        qDebug() << "avcodec_alloc_context3 fail";
        return;
    }

    codecContext->sample_fmt = pcmSpec.sampleFormat;
    codecContext->channel_layout = pcmSpec.channelLayout;
    codecContext->channels = av_get_channel_layout_nb_channels(pcmSpec.channelLayout);
    codecContext->sample_rate = pcmSpec.sampleRate;
    // 设置转换 bit rate
    codecContext->bit_rate = 32000;
    // 设置 aac 规格
    codecContext->profile = FF_PROFILE_AAC_HE_V2;

    /*
     *  检测格式是否支持
    */
    if(!checkSampleFormat(codec,pcmSpec.sampleFormat)) {

        qDebug() << "sample format : " << av_get_sample_fmt_name(pcmSpec.sampleFormat) << " not support";
        // 进行音频重采样
        return;
    }


    // 设置 fdk_aac 单独的参数设置
    AVDictionary* dict = nullptr;
    av_dict_set(&dict,"vbr","1",0);

    int res = avcodec_open2(codecContext,codec,nullptr);

    if (res != 0) {

        avcodec_free_context(&codecContext);
        ERRMSG(res);
        qDebug() << "avcodec_open2 fail" << errBuf;

        return;
    }

    AVFrame* frame = av_frame_alloc();

    if(frame == nullptr) {


//        avcodec_close(codecContext);
        avcodec_free_context(&codecContext);
        qDebug() << "av_frame_alloc fail";

        return;
    }

    qDebug() <<  codecContext->frame_size;

    frame->nb_samples = codecContext->frame_size;
    frame->channel_layout = pcmSpec.channelLayout;
    frame->format = pcmSpec.sampleFormat;

    res = av_frame_get_buffer(frame,0);

    if(res < 0) {


        avcodec_free_context(&codecContext);
        ERRMSG(res);
        qDebug() << "av_frame_get_buffer fail " << errBuf;
        return;
    }


    AVPacket* packet = av_packet_alloc();

    if(packet == nullptr) {


        av_frame_free(&frame);
        avcodec_free_context(&codecContext);

        qDebug() << "av_packet_all fail";
        return;
    }


    QFile inFile(pcmSpec.inFilePath);

    if(!inFile.open(QFile::ReadOnly)) {

        av_frame_free(&frame);
        av_packet_free(&packet);
        avcodec_free_context(&codecContext);
        qDebug() << "open " << pcmSpec.inFilePath << " fail";
        return;
    }

    QFile outFile(pcmSpec.outFilePath);

    if(!outFile.open(QFile::WriteOnly)) {

        inFile.close();
        av_frame_free(&frame);
        av_packet_free(&packet);
        avcodec_free_context(&codecContext);
        qDebug() << "open " << pcmSpec.outFilePath << " fail";
        return;
    }



    int len = 0;

    while ((len = inFile.read((char *)frame->data[0],frame->linesize[0])) > 0) {


        if (len < frame->linesize[0]) {

            int channelNum = av_get_channel_layout_nb_channels(pcmSpec.channelLayout);
            int bytes = av_get_bytes_per_sample(pcmSpec.sampleFormat);
            frame->nb_samples = len / (pcmSpec.sampleRate * channelNum * bytes);
        }

        if (encode(codecContext,frame,packet,outFile) < 0) {

            qDebug() << "encode error";
            break;
        }
    }

    /**
     * @brief encode
     * 刷新缓存
     */
    encode(codecContext,nullptr,packet,outFile);

    inFile.close();
    outFile.close();

    av_packet_free(&packet);
    av_frame_free(&frame);
    avcodec_free_context(&codecContext);
}

void FFMEPGUtils::pcmAACDecode(PCMSpec &pcmSpec)
{

    char* aac_name = "libfdk_aac";

    QFile inFile(pcmSpec.inFilePath);
    QFile outFile(pcmSpec.outFilePath);

    AVCodecContext* codecContext = nullptr;
    AVFrame* frame = nullptr;
    AVPacket* packet = nullptr;

    int res;

    char* in_buf_data = new char[IN_DATA_LEN + AV_INPUT_BUFFER_PADDING_SIZE];
    char* in_buf = in_buf_data;

    int in_len = 0;
    bool read_finish = false;


    AVCodec* codec = avcodec_find_decoder_by_name(aac_name);

    if(codec == nullptr) {


        qDebug() << "not find " << aac_name;
        return;
    }

    AVCodecParserContext* parserContext = av_parser_init(codec->id);

    if (parserContext == nullptr) {

        qDebug() << "av_parser_init fail";
        goto end;
    }

    codecContext = avcodec_alloc_context3(codec);

    if(codecContext == nullptr) {

        qDebug() << "avcodec_alloc_context3 fail";
        goto end;
    }


    res = avcodec_open2(codecContext,codec,nullptr);

    if(res != 0) {

        ERRMSG(res);
        qDebug() << "avcodec_open2 fail " << errBuf;
        goto end;
    }

    frame = av_frame_alloc();

    if(frame == nullptr) {

        qDebug() << "av_frame_alloc fail";
        goto end;
    }

    packet = av_packet_alloc();

    if(packet == nullptr) {

        qDebug() << "av_packet_alloc fail";
        goto end;
    }


    if(!inFile.open(QFile::ReadOnly)) {

        qDebug() << "open " << pcmSpec.inFilePath << " fail";
        goto end;
    }

    if(!outFile.open(QFile::WriteOnly)) {

        qDebug() << "open " << pcmSpec.outFilePath << " fail";
        goto end;
    }

    in_len = inFile.read((char*)in_buf,IN_DATA_LEN);

    while (in_len > 0) {

        /**
         * Example:
         * @code
         *   while(in_len){
         *       len = av_parser_parse2(myparser, AVCodecContext, &data, &size,
         *                                        in_data, in_len,
         *                                        pts, dts, pos);
         *       in_data += len;
         *       in_len  -= len;
         *
         *       if(size)
         *          decode_frame(data, size);
         *   }
         * @brief av_parser_parse2
         * int av_parser_parse2(AVCodecParserContext *s,
                     AVCodecContext *avctx,
                     uint8_t **poutbuf, int *poutbuf_size,
                     const uint8_t *buf, int buf_size,
                     int64_t pts, int64_t dts,
                     int64_t pos);
         */
        int len = av_parser_parse2(parserContext,codecContext,
                                   &packet->data,&packet->size,
                                   (uint8_t *)in_buf,in_len,
                                   AV_NOPTS_VALUE,AV_NOPTS_VALUE,0);

        in_len -= len;
        in_buf += len;

        if(packet->size > 0 && decode(codecContext,packet,frame,outFile) != 0) {

            qDebug() << "decode fail";
            goto end;
        }

        if(in_len < REFILL_THREF && !read_finish) {

            memmove(in_buf_data,in_buf,in_len);

            in_buf = in_buf_data;

            int read_len = inFile.read(in_buf+in_len,IN_DATA_LEN - in_len);

            if(read_len <= 0) {

                read_finish = true;
            }else {

                in_len += read_len;
            }

        }
    }

    pcmSpec.channelLayout = codecContext->channel_layout;
    pcmSpec.sampleRate = codecContext->sample_rate;
    pcmSpec.sampleFormat = codecContext->sample_fmt;


end:

    if(in_buf_data != nullptr) {

        delete [] in_buf_data;
        in_buf_data = nullptr;
    }

    inFile.close();
    outFile.close();
    av_packet_free(&packet);
    av_frame_free(&frame);
    avcodec_free_context(&codecContext);


}
